# storage/slotted_page.py

import struct
from .page import Page, PAGE_SIZE

# 定义页头和槽的二进制格式
# I: unsigned int (4 bytes)
HEADER_FORMAT = 'II'  # (num_records, free_space_pointer)
HEADER_SIZE = struct.calcsize(HEADER_FORMAT)

SLOT_FORMAT = 'II'  # (record_offset, record_length)
SLOT_SIZE = struct.calcsize(SLOT_FORMAT)


class SlottedPage:
    """
    一个包装类，用于将一个 Page 对象 解析为槽页。
    它不拥有数据，只提供对传入 Page.data 的结构化访问。
    """

    def __init__(self, page: Page):
        self.page = page
        self.data = page.data  # 直接引用页面的 bytearray
        # Auto-initialize header if this page is brand-new (all zeros)
        num_records, free_space_pointer = struct.unpack_from(HEADER_FORMAT, self.data, 0)
        if num_records == 0 and free_space_pointer == 0:
            # Set free_space_pointer to end of page initially
            self._set_header(0, PAGE_SIZE)

    def _get_header(self):
        """解析并返回页头信息。"""
        num_records, free_space_pointer = struct.unpack_from(HEADER_FORMAT, self.data, 0)
        return num_records, free_space_pointer

    def _set_header(self, num_records: int, free_space_pointer: int):
        """更新页头信息。"""
        struct.pack_into(HEADER_FORMAT, self.data, 0, num_records, free_space_pointer)
        self.page.is_dirty = True  # 任何修改都应将页面标记为脏页

    def _get_slot(self, slot_id: int):
        """获取指定槽的信息。"""
        slot_offset = HEADER_SIZE + slot_id * SLOT_SIZE
        record_offset, record_length = struct.unpack_from(SLOT_FORMAT, self.data, slot_offset)
        return record_offset, record_length

    def _set_slot(self, slot_id: int, record_offset: int, record_length: int):
        """更新指定槽的信息。"""
        slot_offset = HEADER_SIZE + slot_id * SLOT_SIZE
        struct.pack_into(SLOT_FORMAT, self.data, slot_offset, record_offset, record_length)
        self.page.is_dirty = True

    def get_free_space(self) -> int:
        """计算并返回可用的连续空闲空间大小。"""
        num_records, free_space_pointer = self._get_header()
        # 空闲空间是槽数组末端和记录数据区始端之间的区域
        return free_space_pointer - (HEADER_SIZE + num_records * SLOT_SIZE)

    def insert_record(self, record_data: bytes) -> int or None:
        """
        在页面中插入一条新记录。
        成功则返回 slot_id，失败则返回 None。
        """
        record_len = len(record_data)
        if self.get_free_space() < record_len + SLOT_SIZE:
            return None  # 空间不足

        num_records, free_space_pointer = self._get_header()

        # 计算新记录的存储位置（从后往前）
        new_record_offset = free_space_pointer - record_len
        self.data[new_record_offset:free_space_pointer] = record_data

        # 在槽数组中为新记录分配一个槽
        new_slot_id = num_records
        self._set_slot(new_slot_id, new_record_offset, record_len)

        # 更新页头
        self._set_header(num_records + 1, new_record_offset)

        return new_slot_id

    def get_record(self, slot_id: int) -> bytes or None:
        """根据 slot_id 读取记录。"""
        num_records, _ = self._get_header()
        if slot_id >= num_records:
            return None  # slot_id 无效

        offset, length = self._get_slot(slot_id)
        if length == 0:
            return None  # 记录已被删除

        return self.data[offset:offset + length]

    def delete_record(self, slot_id: int) -> bool:
        """删除一条记录（通过将其长度置为0来标记）。"""
        num_records, _ = self._get_header()
        if slot_id >= num_records:
            return False

        offset, length = self._get_slot(slot_id)
        if length == 0:
            return False # 已经被删除了

        self._set_slot(slot_id, 0, 0) # 标记为删除
        return True